iT邦幫忙

2022 iThome 鐵人賽

DAY 26
1

實作時感覺最魔術的地方是 flex-direction: row-reverse; 搭配 justify-content: flex-end; 來做出堆疊效果。其他基本上都是小事。

成品

https://ithelp.ithome.com.tw/upload/images/20221007/20142759isKeXj1tYY.png

原始碼
成品

開發思路

Avatar

首先參考一下 MUI Avatar 的使用方式,可以透過 props.src 來指定圖片,或是也可將內容當做 children 包進元件。

為了實現以上需求,採用以下的條件來決定最後 Avatar 元件究竟要渲染什麼內容:

const finalRender = useMemo(() => {
  if (onError) {
    return (
      <div
        className={cn(imgStyle, withBorder && figureStyle, withChildrenStyle)}
      >
        <PersonIcon fill="#fff" />
      </div>
    );
  }
  if (children) {
    return (
      <div
        className={cn(
          imgStyle,
          withBorder && figureStyle,
          withChildrenStyle,
          withBorder && withChildrenBorderStyle
        )}
      >
        {children}
      </div>
    );
  }
  if (src) {
    return (
      <ImageBase
        src={src}
        classes={{
          figure: cn(withBorder && figureStyle, imgStyle),
          img: imgStyle,
          onError: onErrorStyle,
        }}
        onError={fallBackToText}
      />
    );
  }
  return (
    <div className={cn(imgStyle, withBorder && figureStyle, withChildrenStyle)}>
      <PersonIcon fill="#fff" />
    </div>
  );
}, [children, src, onError, withBorder, fallBackToText]);

回傳內容的優先度由高到低排序如下:

  1. 若圖片在載入時發生錯誤,使用 <PersonIcon fill="#fff" /> 來作為圖片死去的替代顯示。
  2. 而當使用者有對 Avatar 傳入 props.children 時,顯示該 children 內容。
  3. 使用者有傳入 props.src 時,透過 ImageBase 元件來處理圖片內容。
  4. 最後則是在既沒有 props.children 也沒有提供 props.src 的情況下,回傳(與圖片出錯時一致的)預設外觀。

AvatarGroup

概念:將 props.children 傳遞進來的內容複製一份到 result 陣列裡,如果使用者有設定 props.max 的話,則將超過 max 數值的 Avatar 元件捨去,並透過 result.push(<Avatar withBorder>+{childrenLength - (max - 1)}</Avatar>); 顯示「扣掉 props.max 後,還有多少個 Avatar 元件」此數值。

最後透過 reverse() 反轉 result 陣列,再搭配 flex-direction: row-reverse;justify-content: flex-end; 讓 Avatar 元件們可以正確排序,最後使用 baseStyle 中的 & > div: { marginLeft: '-12px' } 做出元件堆疊的效果。

useEffect(() => {
  let result: JSX.Element[] = [];
  React.Children.forEach(children, (child) => {
    const c = child as JSX.Element;
    result.push(React.cloneElement(c, { withBorder: true }));
  });
  const childrenLength = React.Children.count(children);
  if (typeof max === 'number' && max > 1 && max < childrenLength) {
    result = result.slice(0, max - 1);
    result.push(<Avatar withBorder>+{childrenLength - (max - 1)}</Avatar>);
  }
  setChildrenArr(result.reverse());
}, [children, max]);

修改指南

  • 更改 Avatar 的預設顯示的內容:將 PersonIcon 改成其他圖片或是文字來替代。
  • 更改 AvatarGroup 每一個 Avatar 的間距:這部分是透過 withChildrenBorderStyleborderColor 設定的與背景顏色一致來做出距離效果。如果你複製貼上今天的 code 且專案的背景顏色不是 #f9f4ef 的話,記得將 withChildrenBorderStyle 中的 #f9f4ef 改成正確的背景色。

自評

一直以來都以為 row-reverse 頂多拿來對應一些奇妙的排版邊際案例,沒想到還可以這樣用...?


上一篇
day25: Input
下一篇
day27: Badge
系列文
我們可以不要 component library 了嗎?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言